from .records import *
from .geometry import calculate_geometry
from .util import pad_bytes
from .constant import INODE_TYPE_DIR
import logging, os, time

def format(path):
    logger = logging.getLogger('cpfs.format')

    dev = open(path, "r+b")
    dev.seek(0, os.SEEK_END)
    block_size = 512
    block_count = dev.tell() // block_size

    bootrec = BootRecord(
        fsident='cpfs1'.encode('utf-8'),
        blkcount=block_count,
        blksize=block_size,
        serial=int(time.time()))
    dev.seek(0)
    dev.write(bootrec.pack())
    logger.debug('Wrote %s', bootrec)

    geo = calculate_geometry(block_count, block_size)
    del block_count, block_size
    logger.debug(geo)

    dev.seek(geo.bitmap_start * geo.block_size)
    for blkno in range(geo.bitmap_start, geo.bitmap_end - 1):
        dev.write(b'\0' * geo.block_size)
    excess_bits = geo.bitmap_blocks * geo.bitmap_density - geo.data_blocks
    used_bits = geo.bitmap_density - excess_bits
    last_bitmap_block = bytearray(used_bits // 8)
    if excess_bits % 8:
        assert used_bits % 8 + excess_bits % 8 in [0, 8]
        last_bitmap_block.append(~((1 << (used_bits % 8)) - 1) & 0xff)
    last_bitmap_block.extend([0xff] * (excess_bits // 8))
    assert len(last_bitmap_block) == geo.block_size, len(last_bitmap_block)
    last_unset_bit = used_bits - 1
    byte = last_bitmap_block[last_unset_bit // 8]
    assert not byte & (1 << (last_unset_bit % 8)), hex(byte)
    first_set_bit = used_bits
    byte = last_bitmap_block[first_set_bit // 8]
    assert byte & (1 << (first_set_bit % 8)), hex(byte)
    dev.write(last_bitmap_block)

    assert dev.tell() == geo.block_size * geo.data_start, (dev.tell(), geo.block_size * geo.data_start)
    t = int(time.time() * 1000000)
    root_inode = Inode(
        ino=geo.data_start, type=INODE_TYPE_DIR, perms=0o777, nlink=2,
        uid=0, gid=0, rdev=0,
        atime=t, mtime=t, ctime=t,
        data=Data(size=0, root=-1, depth=0))
    logging.debug('Writing root inode: %s', root_inode)
    dev.write(pad_bytes(
        geo.block_size,
        root_inode.pack(),
        fill=b'\0'))
    dev.seek(geo.bitmap_start * geo.block_size)
    c, = dev.read(1)
    assert not c & 1, c
    c |= 1
    dev.seek(-1, os.SEEK_CUR)
    dev.write(bytes([c])) # mark the root inode as allocated
